using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using SkinnedModel;


//****************************************************
//  USE Microsoft encoderExpress 4 to capture gameplay
//****************************************************
namespace MechaShark_windows_
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        System.Random generator = new System.Random();
        GraphicsDeviceManager graphics;

        Renderer MSrenderer;
        Camera MScamera;
        List<Projectile> ProjectileList = new List<Projectile>();
        List<Matrix> rockLocationList = new List<Matrix>();
        List<Enemy> enemyList = new List<Enemy>();

        SpriteBatch spriteBatch;

        int timerCount = 0;
        int shotCount = 0;
        int enemyCount = 0;
        int moveCount = 0;

        KeyboardState currentKeyboardState = new KeyboardState();
        MouseState currentMouseState = new MouseState();

        Rectangle backgroundRect;


        Model sharkModel;
        Model groundModel;
        Model tempModel;
        Model laserModel;
        //Model energyBallModel;
        Model enemyDiver;
        List<Model> rockModelList = new List<Model>();

        Texture2D groundTexture;
        Texture2D sharkTexture;
        Texture2D backgroundImage;
        Texture2D rockTexture;
        Texture2D laserTexture;
        //Texture2D energyTexture;
        AnimationPlayer animationPlayer;

        AnimationClip sharkIdle;
        AnimationClip sharkMove;
        AnimationClip sharkFire;
        AnimationClip sharkFlinch;

        Matrix groundWorld;
        




        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            IsMouseVisible = true;
            base.Initialize();


        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            groundModel = Content.Load<Model>("groundModel");
            sharkModel = Content.Load<Model>("sharkMecha_asset");
            laserModel = Content.Load<Model>("fireLaser_01");
            enemyDiver = Content.Load<Model>("diverA_empty_geo");
            //energyBallModel = Content.Load<Model>("energyBall_asset");


            tempModel = Content.Load<Model>("rock_01");
            rockModelList.Add(tempModel);
            tempModel = Content.Load<Model>("rock_02");
            rockModelList.Add(tempModel);
            tempModel = Content.Load<Model>("rock_03");
            rockModelList.Add(tempModel);
            tempModel = Content.Load<Model>("rock_04");
            rockModelList.Add(tempModel);
            tempModel = Content.Load<Model>("rock_05");
            rockModelList.Add(tempModel);
            tempModel = Content.Load<Model>("rock_06");
            rockModelList.Add(tempModel);


            sharkTexture = Content.Load<Texture2D>("sharkMecha_texture");
            laserTexture = Content.Load<Texture2D>("fireLaster_01_texture");
            //energyTexture = Content.Load<Texture2D>("energyBall_texture");
            groundTexture = Content.Load<Texture2D>("ground_shader");
            rockTexture = Content.Load<Texture2D>("rock_shader");
            backgroundImage = Content.Load<Texture2D>("set");


            backgroundRect = new Rectangle(-200, 0, backgroundImage.Bounds.Width, backgroundImage.Bounds.Height);

            groundWorld = Matrix.CreateRotationY(MathHelper.ToRadians(0)) *
                Matrix.CreateRotationX(MathHelper.ToRadians(0)) *
                Matrix.CreateTranslation(0, -25, 0);

            rockLocationList.Add(new Matrix());
            rockLocationList.Add(new Matrix());
            rockLocationList.Add(new Matrix());

            rockLocationList[0] = Matrix.CreateRotationY(MathHelper.ToRadians(0)) *
                Matrix.CreateRotationX(MathHelper.ToRadians(0)) *
                Matrix.CreateTranslation(0, -18, -10);
            rockLocationList[1] = Matrix.CreateRotationY(MathHelper.ToRadians(0)) *
                Matrix.CreateRotationX(MathHelper.ToRadians(20)) *
                Matrix.CreateTranslation(-30, -20, -10);
            rockLocationList[2] = Matrix.CreateRotationY(MathHelper.ToRadians(0)) *
                Matrix.CreateRotationX(MathHelper.ToRadians(0)) *
                Matrix.CreateTranslation(20, -15, 10);

            //add rock wall
            int rand;
            for (int i = 0; i < 10; i++)
            {
                rockLocationList.Add(new Matrix());
                rand = generator.Next(0, 360);
                rockLocationList[i+3] = Matrix.CreateRotationY(MathHelper.ToRadians(rand)) *
                Matrix.CreateRotationX(MathHelper.ToRadians(rand)) *
                Matrix.CreateTranslation(-40 + (i*10), -20, -15);
            }
            
            

            // if skinned
            
            SkinningData sharkModel_skinningData = sharkModel.Tag as SkinningData;

            if (sharkModel_skinningData == null)
                throw new InvalidOperationException
                    ("This model does not contain a SkinningData tag.");

            animationPlayer = new AnimationPlayer(sharkModel_skinningData);

            animationPlayer.RegisteredEvents["Fire"].Add("FireFrame", new AnimationPlayer.EventCallback(FireShot));
            animationPlayer.RegisteredEvents["Flinch"].Add("FlinchFrame", new AnimationPlayer.EventCallback(goToIdle));
            animationPlayer.RegisteredEvents["Move"].Add("MoveFrame", new AnimationPlayer.EventCallback(goToIdle));

            sharkIdle = sharkModel_skinningData.AnimationClips["Idle"];
            sharkFire = sharkModel_skinningData.AnimationClips["Fire"];
            sharkMove = sharkModel_skinningData.AnimationClips["Move"];
            sharkFlinch = sharkModel_skinningData.AnimationClips["Flinch"]; 

            animationPlayer.StartClip(sharkIdle);

            MSrenderer = new Renderer(graphics);
            MScamera = new Camera();

            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            //if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            //    this.Exit();

            // TODO: Add your update logic here
            timerCount++;
            backgroundRect.X++;
            if (backgroundRect.X >= -5) { backgroundRect.X = -200; }
            HandleInput();

            if (MScamera.Update(gameTime, currentKeyboardState))
            {
                if (timerCount > moveCount + 20)
                {
                    moveCount = timerCount;
                    animationPlayer.StartClip(sharkMove);
                }
            }

            if (timerCount > enemyCount + 300)
            {
                enemyCount = timerCount;
                float randY = generator.Next(-15, 20);
                float randDist = generator.Next(-30, 20);
                enemyList.Add(new Enemy (enemyDiver, -60, randY, .1f, 8, randDist) );
            }


            animationPlayer.Update(gameTime.ElapsedGameTime, true, Matrix.Identity);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);



            // TODO: Add your drawing code here

            Matrix[] bones = animationPlayer.GetSkinTransforms();

            //------------------2D Mode------------------------
            spriteBatch.Begin();

            spriteBatch.Draw(backgroundImage, backgroundRect, Color.White);

            spriteBatch.End();
            GraphicsDevice.BlendState = BlendState.Opaque;
            GraphicsDevice.DepthStencilState = DepthStencilState.Default; 
            GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
            //----------------3D mode------------------------
            MScamera.ComputeMatricesToDraw(GraphicsDevice);
            MSrenderer.DrawWithSkin(sharkModel, bones, MScamera.currentWorld, MScamera.currentView, MScamera.currentProj);
           // MSrenderer.DrawWithTexture(sharkModel, sharkTexture, MScamera.currentWorld, MScamera.currentView, MScamera.currentProj);
            MSrenderer.DrawWithTexture(groundModel, groundTexture, groundWorld, MScamera.currentView, MScamera.currentProj);

            for (int i = 0; i < rockLocationList.Count; i++)
            {
                if (i > 5)
                {
                    MSrenderer.DrawWithTexture(rockModelList[3], rockTexture, rockLocationList[i], MScamera.currentView, MScamera.currentProj);
                }
                else
                {
                    MSrenderer.DrawWithTexture(rockModelList[i], rockTexture, rockLocationList[i], MScamera.currentView, MScamera.currentProj);
                }
            }
            //MSrenderer.DrawWithTexture(groundModel, groundTexture,  groundWorld, MScamera.currentView, MScamera.currentProj);

            for (int i = 0; i < enemyList.Count; i++)
            {
                if (enemyList[i].DistToMove > enemyList[i].xpos)
                {
                    enemyList[i].moveEnemy(GraphicsDevice, gameTime);
                }
                if (CollideCheckRect(enemyList[i].xpos, enemyList[i].ypos, MScamera.xpos, MScamera.ypos, 14, 20))
                {
                    animationPlayer.StartClip(sharkFlinch);
                }
                if (enemyList[i].Health <= 0)
                {
                    enemyList.Remove(enemyList[i]);
                }
                else
                {
                    if (enemyList[i].IsHurt)
                    {
                        enemyList[i].IsHurt = false;
                        MSrenderer.DrawWithHurtTexture(enemyList[i].Model, sharkTexture, enemyList[i].currentWorld, MScamera.currentView, MScamera.currentProj);
                    }
                    else
                        MSrenderer.DrawWithTexture(enemyList[i].Model, sharkTexture, enemyList[i].currentWorld, MScamera.currentView, MScamera.currentProj);
                }
            }

            for (int i = 0; i < ProjectileList.Count; i++) //draw all projectiles
            {
                if (
                    (- (60) < ProjectileList[i].xpos) &&
                    (ProjectileList[i].xpos < 60)
                    )
                {
                    for (int j = 0; j < enemyList.Count; j++)
                    {
                        if (CollideCheckRect(enemyList[j].xpos, enemyList[j].ypos, ProjectileList[i].xpos, ProjectileList[i].ypos, 10, 18))
                        {
                            enemyList[j].EnemyHurt(1); //1 damage
                            ProjectileList[i].xpos = -70;
                        }
                    }
                    ProjectileList[i].moveProjectile(GraphicsDevice, gameTime);
                    MSrenderer.DrawWithTexture(ProjectileList[i].Model, laserTexture, ProjectileList[i].currentWorld, MScamera.currentView, MScamera.currentProj);
                }
                else
                {
                    ProjectileList[i].moveProjectile(GraphicsDevice, gameTime);
                    ProjectileList.Remove(ProjectileList[i]);
                }

            }

            
            base.Draw(gameTime);
        }

        #region Handle Input
        /// <summary>
        /// goes to idle
        /// </summary>
        private void goToIdle(string EventName)
        {
            animationPlayer.StartClip(sharkIdle);
        }

        /// <summary>
        /// fires a shot
        /// </summary>
        private void FireShot(string EventName)
        {
            if ((10 < MScamera.ypos) && (MScamera.ypos < 35))
            {
                ProjectileList.Add(new Projectile(laserModel, MScamera.xpos - 14f, MScamera.ypos + 6.0f, 1));// add new shot at player pos
            }
            else if ((-30 < MScamera.ypos) && (MScamera.ypos < -10))
            {
                ProjectileList.Add(new Projectile(laserModel, MScamera.xpos - 14f, MScamera.ypos + 3.2f, 1));// add new shot at player pos
            }
            else
            {
                ProjectileList.Add(new Projectile(laserModel, MScamera.xpos - 14f, MScamera.ypos + 4.2f, 1));// add new shot at player pos
            }
            animationPlayer.StartClip(sharkIdle);
        }

        /// <summary>
        /// Handles input
        /// </summary>
        private void HandleInput()
        {
            currentKeyboardState = Keyboard.GetState();
            currentMouseState = Mouse.GetState();

            if (currentKeyboardState.IsKeyDown(Keys.Space))
            {
                if (timerCount >= shotCount + 20)
                {
                    shotCount = timerCount;

                    animationPlayer.StartClip(sharkFire);
                }
            }


            // Check for exit.
        }
        private bool CollideCheckRect(float posX, float posY, float posCenterX, float posCenterY, float width, float height)
        {
            float leftBound = posX - width/2;
            float rightBound = posX + width/2;
            float topBound = posY + height;
            float bottomBound = posY - height/2;
            if ((leftBound < posCenterX) && (posCenterX < rightBound) &&
                (bottomBound < posCenterY) && (posCenterY < topBound))
            {
                return true;
            }
            else
                return false;
        }

        #endregion
    }
}
